﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Microsoft.Xna.Framework;
using DcsGauges.DataStructures;
using DcsGauges.Gui.Rendering.Interfaces;
using Microsoft.Xna.Framework.Graphics;
using DcsGauges.Input.Events;

namespace DcsGauges.Gui.Rendering
{
    abstract class GuiRenderableBase : IGuiRenderable
    {
        protected enum ScaleMode
        {
            KeepAspectShrink                            = 0,
        }

        #region Fields / Properties

        protected bool                                  keepAspectRatio                 = true;
        protected GuiRenderableBaseCollection           children                        = new GuiRenderableBaseCollection();

        protected GuiRenderableBase                     Parent                          { get; set; }

        public Rectangle                                ViewportRect                    { get; private set; } 
        public bool                                     Visible                         { get; set; }
        public URect                                    Area                            { get; private set; }

        #endregion


        #region Constructors
        
        public GuiRenderableBase(GuiRenderableBase parent, URect area)
        {
            this.Area               = area;
            this.Visible            = true;

            if (parent != null)
            {
                parent.children.Add(this);
            }

            this.Parent             = parent;
        }

        #endregion


        #region Helpers / Internals

        #region Layouting
        
        protected Rectangle CalcViewportRect(Rectangle parentRectangle, Rectangle? originalDimensions = null)
        {
            Rectangle result = new Rectangle(
                parentRectangle.Left + (int)((float)parentRectangle.Width * this.Area.TopLeft.X),
                parentRectangle.Top + (int)((float)parentRectangle.Height * this.Area.TopLeft.Y),
                (int)((float)parentRectangle.Width * this.Area.Size.Width),
                (int)((float)parentRectangle.Height * this.Area.Size.Height));

            if(this.keepAspectRatio && originalDimensions.HasValue)
            {
                Rectangle od = originalDimensions.Value;

                float widthScaleFactor  = (float)result.Width / (float)od.Width;
                float heightScaleFactor = (float)result.Height / (float)od.Height;

                if(widthScaleFactor < heightScaleFactor)
                {
                    result.Width = (int)((float)od.Width * widthScaleFactor);
                    result.Height = (int)((float)od.Height * widthScaleFactor);
                }
                else
                {
                    result.Width = (int)((float)od.Width * heightScaleFactor);
                    result.Height = (int)((float)od.Height * heightScaleFactor);
                }
            }

            this.ViewportRect = result; //save the current viewport rect for other operations that require a most actual screen rect

            return result;
        }

        protected Vector2 CalcScaleToFitRect(Rectangle desiredRect, Rectangle originalRect, ScaleMode mode = ScaleMode.KeepAspectShrink)
        {
            float scaleX                    = (float)desiredRect.Width / (float)originalRect.Width;
            float scaleY                    = (float)desiredRect.Height / (float)originalRect.Height;

            if (mode == ScaleMode.KeepAspectShrink)
            {
                float scale                 = Math.Min(scaleX, scaleY);
                return new Vector2(scale, scale);
            }

            throw new ArgumentException("Invalid Scale mode");
        }

        protected Rectangle ScaleRect(Rectangle source, Vector2 scale)
        {
            Rectangle rect                  = new Rectangle(source.X, source.Y, source.Width, source.Height);
            rect.Width                      = (int)((float)source.Width * scale.X);
            rect.Height                     = (int)((float)source.Height * scale.Y);
            return rect;
        }

        protected Rectangle CalcViewportRect(URect area, Rectangle parentRectangle, Rectangle? originalDimensions = null)
        {
            Rectangle result = new Rectangle(
                parentRectangle.Left + (int)((float)parentRectangle.Width * area.TopLeft.X),
                parentRectangle.Top + (int)((float)parentRectangle.Height * area.TopLeft.Y),
                (int)((float)parentRectangle.Width * area.Size.Width),
                (int)((float)parentRectangle.Height * area.Size.Height));

            if(this.keepAspectRatio && originalDimensions.HasValue)
            {
                Rectangle od = originalDimensions.Value;

                float widthScaleFactor  = (float)result.Width / (float)od.Width;
                float heightScaleFactor = (float)result.Height / (float)od.Height;

                if(widthScaleFactor < heightScaleFactor)
                {
                    result.Width = (int)((float)od.Width * widthScaleFactor);
                    result.Height = (int)((float)od.Height * widthScaleFactor);
                }
                else
                {
                    result.Width = (int)((float)od.Width * heightScaleFactor);
                    result.Height = (int)((float)od.Height * heightScaleFactor);
                }
            }

            return result;
        }

        #endregion


        #region Layering
        
        protected float NextBackIndex(float curIndex, int n = 1)
        {
            return curIndex - 0.01f * (float)n;
        }

        protected float NextFrontIndex(float curIndex, int n = 1)
        {
            return curIndex + 0.01f * (float)n;
        }

        #endregion

        #endregion


        #region Overrides

        public abstract void Render(Microsoft.Xna.Framework.Graphics.SpriteBatch spriteBatch, GraphicsDevice device, Rectangle parentRect, float globalZIndex);


        public virtual void InjectMouseEvent(MouseEvent evt, GameTime gt)
        {
            if (!evt.WasHandeled)
            {
                this.children.InjectMouseEvent(evt, gt);
            }
        }

        public virtual void InjectKeyboardEvent()
        {
            throw new NotImplementedException();
        }

        public virtual void InjectTouchEvent()
        {
            throw new NotImplementedException();
        }

        #endregion
    }

    #region BusinessCollections

    internal class GuiRenderableBaseCollection : List<IGuiRenderable>
    {
        public void InjectMouseEvent(MouseEvent evt, GameTime gt)
        {
            foreach (var item in this)
            {
                item.InjectMouseEvent(evt, gt);
            }
        }

        public void Render(SpriteBatch spriteBatch, GraphicsDevice device, Microsoft.Xna.Framework.Rectangle parentRect, float globalZIndex)
        {
            foreach (var item in this)
            {
                item.Render(spriteBatch, device, parentRect, globalZIndex);
            }
        }
    }

    #endregion
}
